<?php
/**
 * 绮梦PHP框架 (Dream PHP Freamework)
 * 免费 简单 快速 高效 安全 专业
 * 绮梦PHP框架是一个基于组件的PHP框架，用于快速开发现代Web应用程序。
 *
 * @author lewis (lewisld@163.com)
 * @copyright Copyright (c) 2015, lewis (http://www.dreamfreamework.com/)
 * @license http://www.dreamfreamework.com/licenses/ Apache License
 * @link http://www.dreamfreamework.com/
 */

namespace System\Basic;

use System\Exception\handler;

class Kernel extends Object
{

    /**
     * 框架是否已经初始化 是：TRUE 否：FALSE
     *
     * @var bool
     */
    protected static $init = FALSE;

    /**
     * 是否开始框架错误异常的捕获于显示 是：TRUE 否：FALSE
     *
     * @var bool
     */
    protected static $error = TRUE;

    /**
     * 系统字符集 默认值：utf-8
     *
     * @var string
     */
    protected static $charset = 'utf-8';

    /**
     * 框架使用环境 开发环境：develop 测试环境：testing 部署环境：Staging
     *
     * @var string
     */
    protected static $environment = 'develop';

    /**
     * 是否在Windows环境中运行
     *
     * @var bool
     */
    protected static $isWindows;

    /**
     * 是否在Windows环境中运行
     *
     * @var bool
     */
    protected static $isSafeMode;

    /**
     * 初始化框架运行 框架运行时的初始化。
     *
     * @static
     *
     * @access public
     * @return void 没有返回值
     */
    public static function init(array $setting = [])
    {
        if (self::$init) {
            return;
        } // 不允许执行两次框架的初始化

        self::$init = TRUE; // 设置框架为已经初始化

        // 确定是否在Windows环境中运行
        self::$isWindows = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? TRUE : FALSE;

        self::$isSafeMode = (bool) ini_get('safe_mode'); // 确定是否在安全模式下运行

        if (isset($setting['error'])) {
            // 设置错误和异常的处理
            self::$error = (bool) $setting['error'];
        }

        if (TRUE === self::$error) {
            // 使用 “\System\Exception\handler” 异常处理,增加了堆栈跟踪和误差来源
            handler::registerExceptionHandler([
                '\System\Exception\handler',
                'registerExceptionHandler'
            ]);

            // 使用 “\System\Exception\handler” 错误处理,将所有PHP错误转换为例外
            handler::registerErrorHandler([
                '\System\Exception\handler',
                'registerErrorHandler'
            ]);
        }

        if ('develop' == self::$environment and extension_loaded('xdebug')) {
            ini_set('xdebug.collect_params', 3);
        }

        // 使用 “\System\Exception\handler” 处理应用程序关闭，捕获致命错误
        register_shutdown_function([
            '\System\Exception\handler',
            'registerShutdownHandler'
        ]);

        if (ini_get('register_globals')) {
            // 清理全局变量
            self::globals();
        }

        if (isset($setting['charset'])) {
            // 设置系统字符集
            self::$charset = strtoupper($setting['charset']);
        }

        if (function_exists('mb_internal_encoding')) {
            // 设置内部字符编码
            mb_internal_encoding(self::$charset);
        }

        // 清洁所有请求变量
        $_GET = self::sanitize($_GET);
        $_POST = self::sanitize($_POST);
        $_COOKIE = self::sanitize($_COOKIE);
    }

    /**
     * 清理全局变量
     *
     * @static
     *
     * @access public
     * @return void
     */
    public static function globals()
    {
        if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
            // 防止恶意全局重载攻击
            echo '全局变量过载检测到攻击!请求中止。\n';
            // 退出一个错误状态
            exit(1);
        }
        // 得到所有全局变量的变量名
        $globalVariables = array_keys($GLOBALS);
        // 从列表中删除标准的全局变量
        $globalVariables = array_diff($globalVariables, [
            '_COOKIE',
            '_ENV',
            '_GET',
            '_FILES',
            '_POST',
            '_REQUEST',
            '_SERVER',
            '_SESSION',
            'GLOBALS'
        ]);
        foreach ($globalVariables as $name) {
            // 设置全局变量,有效地禁用注册全局变量
            unset($GLOBALS[$name]);
        }
    }

    /**
     * 递归地过滤一个输入的变量 如果启用了魔术引号，反斜线将被去除，但是两个反斜线将会被替换成一个。 标准化换行为 “LF”
     *
     * @static
     *
     * @access public
     * @param mixed $value 变量
     * @return mixed 返回变量值
     */
    public static function sanitize($value)
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $key => $val) {
                // 递归地清洁每一个值
                $value[$key] = self::sanitize($val);
            }
        } elseif (is_string($value)) {
            if (TRUE === self::$magic_quotes) {
                // 反引用一个引用字符串
                $value = stripslashes($value);
            }
            if (FALSE !== strpos($value, "\r")) {
                // 标准化换行为 “LF”
                $value = str_replace([
                    "\r\n",
                    "\r"
                ], "\n", $value);
            }
        }

        return $value;
    }

    /**
     * 清楚框架初始化
     *
     * @static
     *
     * @access public
     * @return void
     */
    public static function delInit()
    {
        if (self::$init) {
            if (self::$error) {
                // 回到前一个错误处理程序
                handler::unregisterExceptionHandler([
                    '\System\Exception\handler',
                    'exceptionHandler'
                ]);

                // 回到之前的异常处理程序
                handler::unregisterErrorHandler([
                    '\System\Exception\handler',
                    'errorHandler'
                ]);
            }
            // 改变框架初始化状态为没有初始化
            self::$init = FALSE;
        }
    }

    /**
     *
     * @param unknown $type
     */
    public static function create($type = 'Web', array $param = NULL)
    {
        define(APP_NAME, '');
    }

    /**
     * 确定是否在Windows环境中运行
     *
     * @static
     *
     * @access public
     * @return bool 是则返回真，否则返回假
     */
    public static function isWindows()
    {
        return self::$isWindows;
    }

    /**
     * 确定是否在安全模式下运行
     *
     * @static
     *
     * @access public
     * @return bool 是则返回真，否则返回假
     */
    public static function isSafeMode()
    {
        return self::$isSafeMode;
    }

    /**
     * 获取系统字符集
     *
     * @static
     *
     * @access public
     * @return string 返回系统字符集
     */
    public static function getCharset()
    {
        return self::$charset;
    }
}